package com.imagination.applet.domain.revenue.identity.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.imagination.applet.controller.api.enums.TrainingStatusEnum;
import com.imagination.applet.controller.api.request.Image2caricatureParam;
import com.imagination.applet.controller.api.request.VirtualIdentityImagesParam;
import com.imagination.applet.controller.api.request.VirtualIdentitySubmitParam;
import com.imagination.applet.controller.view.ProductImageVO;
import com.imagination.applet.controller.view.VirtualIdentityVO;
import com.imagination.applet.controller.web.configuration.UserHolder;
import com.imagination.applet.domain.adapter.facechain.FaceChainAdapter;
import com.imagination.applet.domain.aiimage.model.AIImage;
import com.imagination.applet.domain.aiimage.service.AIImageQueryService;
import com.imagination.applet.domain.photo.model.UserPhoto;
import com.imagination.applet.domain.photo.repository.UserPhotoRepository;
import com.imagination.applet.domain.photo.service.UserPhotoQueryService;
import com.imagination.applet.domain.product.model.ProductImage;
import com.imagination.applet.domain.product.repository.ProductImageRepository;
import com.imagination.applet.domain.product.service.ProductImageQueryService;
import com.imagination.applet.domain.revenue.charge.model.StarCharge;
import com.imagination.applet.domain.revenue.identity.model.vo.VirtualIdentityImageResult;
import com.imagination.applet.domain.revenue.identity.service.VirtualIdentityBuildingService;
import com.imagination.applet.domain.revenue.identity.service.VirtualIdentityImageService;
import com.imagination.applet.domain.revenue.order.model.OrderType;
import com.imagination.applet.domain.revenue.order.model.UserOrder;
import com.imagination.applet.domain.revenue.order.service.OrderService;
import com.imagination.applet.domain.support.payment.service.PayService;
import com.imagination.applet.domain.user.model.User;
import com.imagination.applet.domain.user.repository.UserRepository;
import com.imagination.applet.domain.user.service.UserService;
import com.imagination.applet.domain.user_topic.model.UserTopic;
import com.imagination.applet.domain.user_topic.repository.UserTopicRepository;
import com.imagination.applet.domain.virtualidentity.model.UserVirtualIdentity;
import com.imagination.applet.domain.virtualidentity.service.UserVirtualIdentityCommandService;
import com.imagination.applet.infrastructure.common.CommonProperties;
import com.imagination.applet.infrastructure.handler.ServiceException;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Example;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RequiredArgsConstructor
@Service
public class VirtualIdentityServiceImpl implements VirtualIdentityBuildingService, VirtualIdentityImageService {
    private static final Logger logger = LoggerFactory.getLogger(VirtualIdentityServiceImpl.class);

    private final OrderService orderService;
    private final UserPhotoQueryService userPhotoQueryService;
    private final ProductImageQueryService productImageQueryService;
    private final ProductImageRepository productImageRepository;
    private final FaceChainAdapter faceChainAdapter;
    private final UserVirtualIdentityCommandService userVirtualIdentityCommandService;
    private final UserService userService;
    private final AIImageQueryService aiImageQueryService;
    private final UserRepository userRepository;
    private final PayService payService;
    private final UserPhotoRepository userPhotoRepository;

    @Override
    public PrepayWithRequestPaymentResponse prepay() {
        Long userId = UserHolder.getUserId();
        StarCharge starCharge = StarCharge.FIRST;
        UserOrder order = orderService.createOrder(userId, OrderType.VIRTUAL_IDENTITY, starCharge.getPrice(), starCharge.getCurrency(), null);
        Optional<User> user = userService.getByUserId(order.getUserId());
        return payService.prePay2(user.get().getOpenId(),
                order.getTraceNo(), order.getAmount(),
                order.getCurrency(), order.getOrderType().getDescription());
    }

    @Override
    @Transactional
    public VirtualIdentityVO submitNoPay(VirtualIdentitySubmitParam param) {
        Long userId = UserHolder.getUserId();
        // 0.是否可以制作
        check();
        // 1.查询图片
        List<Long> userPhotoIdList = param.getUserPhotoIdList();
        userPhotoIdList.add(param.getUserFacePhotoId());
        List<UserPhoto> userPhotos = userPhotoQueryService.queryUserPhotos(userId, userPhotoIdList);
        // 2.创建虚拟分身
        UserVirtualIdentity virtualIdentity = userVirtualIdentityCommandService.createVirtualIdentity(userId);
        // 3.调用AI训练虚拟分身
        training(userPhotos, virtualIdentity);
        return buildVirtualIdentityVO(virtualIdentity);
    }

    @Override
    public boolean check() {
        Long userId = UserHolder.getUserId();
        // 0.校验是否超过次数
        List<UserVirtualIdentity> userVirtualIdentities = userVirtualIdentityCommandService.queryTrainingSuccessListContainDel(userId);
        Optional<User> user = userService.getByUserId(userId);
        user.ifPresentOrElse(userInfo -> {
            if (userVirtualIdentities.size() >= userInfo.getVirtualIdentityQtyThreshold()) {
                throw new IllegalArgumentException("虚拟分身制作次数不足！");
            }
        }, () -> {
            throw new IllegalArgumentException("用户不存在！");
        });
        // 0.校验是否存在训练中的任务
        List<UserVirtualIdentity> trainingRunning = userVirtualIdentityCommandService.queryTrainingRunning(userId);
        if (!trainingRunning.isEmpty()) {
            throw new IllegalArgumentException("虚拟分身正在制作！");
        }
        return true;
    }

    @Override
    public List<VirtualIdentityVO> fetchVirtualIdentity() {
        Long userId = UserHolder.getUserId();
        List<UserVirtualIdentity> virtualIdentityList = userVirtualIdentityCommandService.queryTrainingSuccessList(userId);
        return virtualIdentityList.stream().map(this::buildVirtualIdentityVO).toList();
    }

    @Override
    public VirtualIdentityVO fetchTrainingVirtualIdentity() {
        Long userId = UserHolder.getUserId();
        List<UserVirtualIdentity> userVirtualIdentities = userVirtualIdentityCommandService.queryTrainingRunning(userId);
        if (!CollectionUtils.isEmpty(userVirtualIdentities)) {
            return buildVirtualIdentityVO(userVirtualIdentities.get(0));
        }
        return null;
    }

    @Override
    public Integer fetchTrainingVirtualIdentityCount() {
        List<UserVirtualIdentity> userVirtualIdentities = userVirtualIdentityCommandService.queryTrainingRunning();
        return userVirtualIdentities.size();
    }

    @Override
    public void remove(Long id) {
        userVirtualIdentityCommandService.remove(id);
    }

    private final CommonProperties commonProperties;

    private final UserTopicRepository userTopicRepository;

    public JSONObject inferenceImages(VirtualIdentityImagesParam param) {
        Long userId = UserHolder.getUserId();
//        Long userId =10000008L;
        String taskId = RandomUtil.randomString(20);
        Optional<UserPhoto> userPhoto = userPhotoRepository.findById(param.getVirtualIdentityId());
        UserPhoto virtualIdentity = userPhoto.get();
        if (!Objects.equals(virtualIdentity.getUserId(), userId)) {
            throw new IllegalArgumentException("照片不正确！");
        }

        AIImage aiImage = aiImageQueryService.queryGeneratedImage(param.getAiImageId());
        Optional<User> user = userService.getByUserId(userId);
        if (!commonProperties.getFlag()) {
            Optional<UserTopic> userTopic = userTopicRepository.findOne(Example.of(UserTopic.builder()
                    .topicId(aiImage.getTopicId())
                    .userId(userId)
                    .build()));
            if (userTopic.isEmpty()) {
                throw new ServiceException("暂未开通该分类，请先开通分类!");
            }
            // 账户消费星星
            if (user.isPresent()) {
                User update = user.get();
                int start = update.getStarCount() - 1;
                if (start < 0) {
                    throw new ServiceException("星星余额不足!");
                }
                update.setStarCount(start);
                userRepository.saveAndFlush(update);
            }
        }


        //生成写真
        JSONObject inference = faceChainAdapter.inference(aiImage, taskId, virtualIdentity.getId(), param.getImagination(), virtualIdentity.getPhotoPath());
        ProductImage entity = new ProductImage();
        entity.setUserId(userId);
        entity.setChannelType(aiImage.getChannelType());
        entity.setVirtualIdentityId(virtualIdentity.getId());
        entity.setAiImageId(param.getAiImageId());
        entity.setTaskId(taskId);
        entity.setIsHd(0);
        entity.setTrainingStatus(TrainingStatusEnum.TRAINING_WAITTING.getCode());
        logger.debug("===========> entity insert: {}", entity);
        productImageRepository.save(entity);
        JSONObject result = new JSONObject();
        result.put("taskId", taskId);
        if (inference.getInteger("code") == 0) {
            JSONObject data = inference.getJSONObject("data");
            result.put("waiting_tasks", data.getInteger("watting_tasks"));
            result.put("waiting_time", data.getInteger("watting_time"));
        } else {
            result.put("waiting_tasks", -1);
            result.put("waiting_time", -1);
        }
        return result;

    }


    @Override
    public JSONObject image2caricature(Image2caricatureParam param) {
        Long userId = UserHolder.getUserId();
        String taskId = RandomUtil.randomString(20);

        AIImage aiImage = aiImageQueryService.queryGeneratedImage(param.getAiImageId());
        Optional<User> user = userService.getByUserId(userId);

        if (!commonProperties.getFlag()) {
            Optional<UserTopic> userTopic = userTopicRepository.findOne(Example.of(UserTopic.builder()
                    .topicId(aiImage.getTopicId())
                    .userId(userId)
                    .build()));
            if (userTopic.isEmpty()) {
                throw new ServiceException("暂未开通该分类，请先开通分类!");
            }

            // 账户消费星星
            if (user.isPresent()) {
                User update = user.get();
                int start = update.getStarCount() - 1;
                if (start < 0) {
                    throw new ServiceException("星星余额不足!");
                }
                update.setStarCount(start);
                userRepository.saveAndFlush(update);
            }
        }
        JSONObject inference = faceChainAdapter.inference(aiImage, taskId, null, null, param.getUrl());

        ProductImage entity = new ProductImage();
        entity.setUserId(userId);
        entity.setChannelType(aiImage.getChannelType());
        entity.setVirtualIdentityId(null);
        entity.setAiImageId(param.getAiImageId());
        entity.setTaskId(taskId);
        entity.setIsHd(0);
        entity.setTrainingStatus(TrainingStatusEnum.TRAINING_WAITTING.getCode());
        logger.debug("===========> entity insert: {}", entity);
        productImageRepository.save(entity);
        JSONObject result = new JSONObject();
        result.put("taskId", taskId);
        if (inference.getInteger("code") == 0) {
            JSONObject data = inference.getJSONObject("data");
            result.put("waiting_tasks", data.getInteger("watting_tasks"));
            result.put("waiting_time", data.getInteger("watting_time"));
        } else {
            result.put("waiting_tasks", -1);
            result.put("waiting_time", -1);
        }
        return result;
    }

    @Override
    public JSONObject imageHD(ProductImage productImage, Integer imagination) {
        Long userId = UserHolder.getUserId();
//        Long userId = 10000005L;

        if (!productImage.getUserId().equals(userId)) {
            throw new IllegalArgumentException("仅可高清化自己的写真！");
        }

        String taskId = RandomUtil.randomString(20);
        AIImage aiImage = aiImageQueryService.queryGeneratedImage(productImage.getAiImageId());


        Optional<User> user = userService.getByUserId(userId);
        if (!commonProperties.getFlag()) {
            // 账户消费星星
            if (user.isPresent()) {
                User update = user.get();
                int start = update.getStarCount() - 1;
                if (start < 0) {
                    throw new ServiceException("星星余额不足!");
                }
                update.setStarCount(start);
                userRepository.saveAndFlush(update);
            }
        }
        //生成高清
        JSONObject jsonObject = faceChainAdapter.imageHD(aiImage, taskId, productImage.getImagePath(), imagination);
        //保存生成记录任务
        ProductImage entity = new ProductImage();
        entity.setUserId(userId);
        entity.setVirtualIdentityId(productImage.getVirtualIdentityId());
        entity.setAiImageId(productImage.getAiImageId());
        entity.setTaskId(taskId);
        entity.setChannelType(productImage.getChannelType());
        entity.setIsHd(1);
        entity.setOriginalPhoto(productImage.getId());
        entity.setTrainingStatus(TrainingStatusEnum.TRAINING_WAITTING.getCode());
        productImageRepository.save(entity);
        JSONObject result = new JSONObject();
        result.put("taskId", taskId);
        if (jsonObject.getInteger("code") == 0) {
            JSONObject data = jsonObject.getJSONObject("data");
            result.put("waiting_tasks", data.getInteger("watting_tasks"));
            result.put("waiting_time", data.getInteger("watting_time"));
        } else {
            result.put("waiting_tasks", -1);
            result.put("waiting_time", -1);
        }
        return result;
    }

    @Override
    public VirtualIdentityImageResult queryImages(String taskId) {
        List<ProductImage> productImageList = productImageQueryService.getByTaskId(taskId);
        logger.debug("[queryImages] Got product image list size: {}", productImageList.size());
        if (productImageList.isEmpty()) {
            return new VirtualIdentityImageResult(TrainingStatusEnum.TRAINING_FAILED.getCode(), 0, 0, Lists.newArrayList(), Boolean.FALSE, 0);
        }
        ProductImage productImage = productImageList.get(0);
        AIImage aiImage = aiImageQueryService.queryGeneratedImage(productImage.getAiImageId());
        VirtualIdentityImageResult result = new VirtualIdentityImageResult();

        if (Objects.equals(productImage.getTrainingStatus(), TrainingStatusEnum.TRAINING_RUNNING.getCode()) || Objects.equals(productImage.getTrainingStatus(), TrainingStatusEnum.TRAINING_WAITTING.getCode())) {
            boolean isHd = productImage.getIsHd() == 1;
            JSONObject aiRsp = faceChainAdapter.inferenceStatus(taskId, aiImage.getModelName(), isHd);
            if (Objects.equals(aiRsp.getInteger("code"), TrainingStatusEnum.TRAINING_RUNNING.getCode())) {
                productImage.setTrainingStatus(TrainingStatusEnum.TRAINING_RUNNING.getCode());
                result.setWaitingTime(aiRsp.getInteger("waitting_time"));
                result.setIsHd(isHd);
                result.setTotalTime(aiRsp.getInteger("total_time"));
                productImageRepository.save(productImage);
            }
            if (Objects.equals(aiRsp.getInteger("code"), TrainingStatusEnum.TRAINING_WAITTING.getCode())) {
                productImage.setTrainingStatus(TrainingStatusEnum.TRAINING_WAITTING.getCode());
                result.setWaitingNum(aiRsp.getInteger("waitting_num"));
            }
            if (Objects.equals(aiRsp.getInteger("code"), TrainingStatusEnum.TRAINING_SUCCESS.getCode())) {
                productImage.setTrainingStatus(TrainingStatusEnum.TRAINING_SUCCESS.getCode());
                JSONArray data = aiRsp.getJSONArray("data");
                for (int i = 0; i < data.size(); i++) {
                    logger.debug("[queryImages] Got inference data size: {}", data.size());
                    if (i == 0) {
                        productImage.setImagePath(data.get(i).toString());
                    } else {
                        ProductImage productImage1 = BeanUtil.copyProperties(productImage, ProductImage.class);
                        productImage1.setId(null);
                        productImage1.setImagePath(data.get(i).toString());
                        productImageList.add(productImage1);
                    }
                }
                logger.debug("[queryImages] After receive inference result, product image list: {}", productImageList.size());
            }
            if (Objects.equals(aiRsp.getInteger("code"), TrainingStatusEnum.TRAINING_FAILED.getCode())) {
                productImage.setTrainingStatus(TrainingStatusEnum.TRAINING_FAILED.getCode());
                productImage.setDeleted(1);
                Optional<User> user = userService.getByUserId(productImage.getUserId());
                // 任务失败补充星星
                if (!commonProperties.getFlag()) {
                    if (user.isPresent()) {
                        User update = user.get();
                        int start = update.getStarCount() + 2;
                        update.setStarCount(start);
                        userRepository.saveAndFlush(update);
                    }
                }
            }
            productImageRepository.saveAllAndFlush(productImageList);
        }
        result.setTrainingStatus(productImage.getTrainingStatus());
        result.setImageList(productImageList.stream().map(p -> ProductImageVO.builder()
                .productImageId(p.getId())
                .taskId(p.getTaskId())
                .trainingStatus(p.getTrainingStatus())
                .link(p.getLink())
                .isHd(p.getIsHd())
                .build()
        ).collect(Collectors.toList()));
        return result;
    }

    private void training(List<UserPhoto> userPhotos, UserVirtualIdentity virtualIdentity) {
        faceChainAdapter.training(virtualIdentity.getId(), userPhotos.stream().map(UserPhoto::getPhotoPath).toList());
    }

    private VirtualIdentityVO buildVirtualIdentityVO(UserVirtualIdentity virtualIdentity) {
        VirtualIdentityVO virtualIdentityVO = new VirtualIdentityVO();
        virtualIdentityVO.setVirtualIdentityId(virtualIdentity.getId());
        virtualIdentityVO.setTrainingStatus(virtualIdentity.getTrainingStatus());

        if (virtualIdentity.getThumbProductImageId() != null) {
            ProductImage productImage = productImageQueryService.getById(virtualIdentity.getThumbProductImageId());
            virtualIdentityVO.setThumb(productImage.getLink());
        }
        return virtualIdentityVO;
    }
}
